package com.to8to.kitt.client;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;

import org.apache.thrift.TApplicationException;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.to8to.kitt.Advice;
import com.to8to.kitt.Constants;
import com.to8to.kitt.utils.LogUtils;

public class ThriftClientInvocationHandler implements InvocationHandler {
	
	public static Logger logger = LoggerFactory.getLogger(ThriftClientInvocationHandler.class);
	
	public static final int RETRY_INTERVAL = 1000;

//	public static final int MAX_DATA_LENGTH = 128 * 1024 * 1024;

	public static final int RETRY_NUM = 3;
	
	private String service;
	
	private ThriftClientConfig config;
	
	private Advice advice;
	
	private ThreadLocal<TServiceClient> threadLocalClients;

	public ThriftClientInvocationHandler(String service, ThriftClientConfig config, Advice advice) {
		this.service = service;
		this.config = config;
		this.advice = advice;
	}
	
	
	public ThriftClientInvocationHandler(String service, String host, int port, int timeoutms, Advice advice) {
		this.service = service;
		this.config = new ThriftClientConfig(null, host, port, timeoutms);
		this.advice = advice;
	}

	private TServiceClient getThreadLocalClient() {
		TServiceClient client = null;
		synchronized (this) {
			if (threadLocalClients == null) 
				threadLocalClients = new ThreadLocal<TServiceClient>();
			
			client = threadLocalClients.get();
		}

		
		if (client == null) 
			client = resetThreadLocalClients();
		
//		TTransport transport = client.getOutputProtocol().getTransport();
		
//		boolean isopen = transport.
		
		return client;
	}
	
	public synchronized void clearThreadLocalClients() {
		threadLocalClients = null;
	}
	
	public synchronized TServiceClient resetThreadLocalClients() {
		logger.info("重置thread的thrift客户端!!! {}-{}", service, config.toString());
		TServiceClient client = createClient();
		if (threadLocalClients == null) 
			threadLocalClients = new ThreadLocal<TServiceClient>();
		threadLocalClients.set(client);
		return client;
	}

	public synchronized TServiceClient createClient() {
		try {

			logger.info("create thrift client ... start!!!");

			String clientClassName = service + "$Client";
			Class<?> cls = Class.forName(clientClassName);
			Constructor<?> constructor = cls.getConstructor(TProtocol.class);

			TTransport transport = new TFramedTransport(new TSocket(config.host, config.port, config.timeoutms), Constants.MAX_DATA_LENGTH);
			transport.open();

			TProtocol protocol = new TBinaryProtocol(transport);
			TServiceClient client = (TServiceClient) constructor.newInstance(protocol);

			logger.info("create thrift client ... success!!!");

			return client;

		} catch (Exception e) {
			logger.info("create thrift client ... failed!!!");
			logger.error(LogUtils.logStackTrace(e));
			throw new RuntimeException(e);
		}
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (advice != null) {
			advice.before(method, args);
		}
		long startTime = System.currentTimeMillis();
		Object result = null;
		try {
			TServiceClient c = getThreadLocalClient();
			result = method.invoke(c, args);
		} catch (Exception e) {
			caughtThrowable(e);
			if (advice != null) {
				advice.tryCatch(e.getCause() != null ? e.getCause() : e);
			}
		}
		long endTime = System.currentTimeMillis();
		if (advice != null)
			advice.after(method, args, endTime - startTime, result);
		return result;
	}
	
	public void caughtThrowable(Exception e) {
		Throwable cause = e.getCause();
		boolean reconnect = true;
		if (cause != null) {
			
			Throwable subcause = cause.getCause();
			
			if(cause instanceof TTransportException && subcause != null && subcause instanceof SocketTimeoutException)
			{
				logger.error("服务器返回超时: SocketTimeoutException!!!\n{}", subcause);
				reconnect = false;
			}
			else if(cause instanceof TApplicationException) //服务器内部异常
			{
				logger.error("服务器内部抛出异常: TApplicationException!!!\n{}", cause);
				reconnect = false;
			}
		}
		
		if(reconnect)
		{
			clearThreadLocalClients();
		}
	}

}